home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / initsnb.zoo / init / sh / test.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-09  |  18.6 KB  |  714 lines

  1. /*
  2.     test.c - test logical expressions containing strings, numbers, and files.
  3.     Copyright (C) 1992 Stephan Neuhaus
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 1, or (at your option)
  8.     any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.     
  19.     You can reach me at neuhaus@informatik.uni-kl.de, or by writing
  20.     paper mail to
  21.     
  22.     Stephan Neuhaus
  23.     Hilgardring 32
  24.     D-6750 Kaiserslautern
  25.     Germany
  26. */
  27.  
  28. #include <compiler.h>
  29. #include <ctype.h>
  30. #include <limits.h>
  31. #include <stdarg.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <unistd.h>
  36. #include <sys/stat.h>
  37. #include <sys/types.h>
  38.  
  39. /* Return value after bad expressions, bad arguments, etc. */
  40. #define EXIT_ERROR 2
  41.  
  42. /* Operators */
  43. #define OP_AND 1    /* Logical and */
  44. #define OP_OR 2        /* Logical or */
  45. #define OP_NOT 3    /* Logical not */
  46.  
  47. #define OP_NUMEQ 4    /* Numeric equality */
  48. #define OP_NUMNE 5    /* Numeric inequality */
  49. #define OP_NUMGE 6    /* Numeric greater than or equal */
  50. #define OP_NUMGT 7    /* Numeric greater than */
  51. #define OP_NUMLE 8    /* Numeric less than or equal */
  52. #define OP_NUMLT 9    /* Numeric less than */
  53.  
  54. #define OP_STREQ 10    /* String equality */
  55. #define OP_STRNE 11    /* String inequality */
  56. #define OP_STRGE 12    /* String greater than or equal */
  57. #define OP_STRGT 13    /* String greater than */
  58. #define OP_STRLE 14    /* String less than or equal */
  59. #define OP_STRLT 15    /* String less than */
  60.  
  61. #define OP_EXFILE 16    /* File exists */
  62. #define OP_REGFILE 17    /* File exists and is a regular file */
  63. #define OP_DIRFILE 18    /* File exists and is a directory file */
  64. #define OP_BLKFILE 19    /* File exists and is a block special file */
  65. #define OP_CHRFILE 20    /* File exists and is a character special file */
  66. #define OP_LNKFILE 21    /* File exists and is a link */
  67. #define OP_FIFFILE 22    /* File exists and is a fifo file */
  68. #define OP_RFILE 23    /* File exists and is a readable file */
  69. #define OP_WFILE 24    /* File exists and is a writable file */
  70. #define OP_XFILE 25    /* File exists and is a executable file */
  71.  
  72.  
  73. /* Token types */
  74. #define TOK_EOF 1
  75. #define TOK_OPERATOR 2
  76. #define TOK_LPAREN 3
  77. #define TOK_RPAREN 4
  78. #define TOK_STRING 6
  79.  
  80. struct token {
  81.   int type;
  82.   union {
  83.     int operator;
  84.     const char *operand;
  85.   } value;
  86. };
  87.  
  88. static struct token curr_token;
  89. static int token_saved;
  90. static int tokens_left;
  91. static int token_index;
  92. static const char *const *tokens;
  93.  
  94. static const char *program_name;
  95.  
  96. static __EXITING error (const char *format, ...);
  97. static void message (const char *format, ...);
  98.  
  99. static void set_tokens (int argc, const char *const *argv);
  100. static int get_token (void);
  101. static void unget_token (void);
  102. static void accept (int type);
  103. static int more_tokens (void);
  104.  
  105. static int expression (void);
  106. static int term (void);
  107. static int factor (void);
  108. static int file_op (int operator, const char *file_name);
  109. static int string_op (int operator, const char *opd1, const char *opd2);
  110. static int num_op (int operator, const char *opd1, const char *opd2);
  111.  
  112. static int is_unary_op (int operator);
  113. static int is_binary_op (int operator);
  114. static int is_string_op (int operator);
  115. static int is_num_op (int operator);
  116. static int is_number (const char *string, int *value);
  117.  
  118. static const char *op_name (int operator);
  119. static const char *type_name (int token_type);
  120. static const char *token_name (const struct token *tok);
  121.  
  122. static int bool_to_status (int boolval);
  123.  
  124. #ifdef DEBUG
  125. static int indent_level = 0;
  126.  
  127. static void indent (void)
  128. {
  129.   int i;
  130.   for (i = 0; i < indent_level; i++)
  131.     fprintf (stderr, "  ");
  132. }
  133.  
  134. static void do_enter (const char *format, ...)
  135. {
  136.   va_list args;
  137.   
  138.   indent ();
  139.   indent_level++;
  140.   fprintf (stderr, "Entering ");
  141.   va_start (args, format);
  142.   vfprintf (stderr, format, args);
  143.   va_end (args);
  144.   fprintf (stderr, "\n");
  145. }
  146.  
  147. static void do_leave (const char *format, ...)
  148. {
  149.   va_list args;
  150.   
  151.   indent_level--;
  152.   indent ();
  153.   fprintf (stderr, "Leaving ");
  154.   va_start (args, format);
  155.   vfprintf (stderr, format, args);
  156.   va_end (args);
  157.   fprintf (stderr, "\n");
  158. }
  159.  
  160. static void do_out (const char *format, ...)
  161. {
  162.   va_list args;
  163.   
  164.   indent ();
  165.   va_start (args, format);
  166.   vfprintf (stderr, format, args);
  167.   va_end (args);
  168.   fprintf (stderr, "\n");
  169. }
  170.  
  171. #  define enter(x) do_enter x
  172. #  define leave(x) do_leave x
  173. #  define out(x) do_out x
  174. #else
  175. #  define enter(x)
  176. #  define leave(x)
  177. #  define out(x)
  178. #endif
  179.  
  180.  
  181. int
  182. main (int argc, const char *const *argv)
  183. {
  184.   int retval;
  185.     
  186.   program_name = argv[0];
  187.  
  188.   set_tokens (argc, argv);
  189.   
  190.   if (get_token () == TOK_EOF)
  191.     retval = 1;        /* Empty expression is true */
  192.   else
  193.     {
  194.       unget_token ();
  195.       retval = expression ();
  196.     }
  197.   
  198.   if (get_token () != TOK_EOF)
  199.     error ("garbage at end of expression");
  200.  
  201.   exit (bool_to_status (retval));
  202.     
  203.   /* NOTREACHED */
  204.   return bool_to_status (retval);
  205. }
  206.  
  207.  
  208. static int
  209. expression (void)
  210. {
  211.   /* Please don't optimize the expression below to
  212.    * `expr_val = expr_val || term()' or some such
  213.    * thing, because then the C short-circuit evalu-
  214.    * ation will bite you in the back, because term()
  215.    * isn't called when expr_val is already true.  Same
  216.    * holds for the code in term().  */
  217.   int term_val;
  218.   int expr_val;
  219.   
  220.   enter (("expression"));
  221.   expr_val = term ();
  222.   while (get_token () == TOK_OPERATOR && curr_token.value.operator == OP_OR)
  223.     {
  224.       out (("expression: or"));
  225.       term_val = term ();
  226.       expr_val = expr_val || term_val;
  227.     }
  228.  
  229.   unget_token ();
  230.   leave (("expression: %d", expr_val));
  231.   return expr_val;
  232. }
  233.  
  234.  
  235. static int
  236. term (void)
  237. {
  238.   int factor_val;
  239.   int term_val;
  240.   
  241.   enter (("term"));
  242.   term_val = factor ();
  243.   while (get_token () == TOK_OPERATOR && curr_token.value.operator == OP_AND)
  244.     {
  245.       out (("term: and"));
  246.       factor_val = factor ();
  247.       term_val = term_val && factor_val;
  248.     }
  249.  
  250.   unget_token ();
  251.   leave (("term: %d", term_val));
  252.   return term_val;
  253. }
  254.  
  255.  
  256. static int
  257. factor (void)
  258. {
  259.   int token_type;
  260.   int retval;
  261.   const char *operand1;
  262.   const char *operand2;
  263.   int file_operator;
  264.   int binary_operator;
  265.     
  266.   enter (("factor"));
  267.   token_type = get_token ();   
  268.   if (token_type == TOK_LPAREN)
  269.     {
  270.       retval = expression ();
  271.       accept (TOK_RPAREN);
  272.     }
  273.   else if (token_type == TOK_OPERATOR)
  274.     {
  275.       if (!is_unary_op (curr_token.value.operator))
  276.         error ("factor: expected unary operator, found %s",
  277.                op_name (curr_token.value.operator));
  278.       if (curr_token.value.operator == OP_NOT)
  279.         retval = !expression ();
  280.       else 
  281.         {
  282.           file_operator = curr_token.value.operator;
  283.           if (get_token () != TOK_STRING)
  284.             error ("factor: expected pathname, found %s",
  285.                    token_name (&curr_token));
  286.           retval = file_op (file_operator, curr_token.value.operand);
  287.         }
  288.     }
  289.   else
  290.     {
  291.       /* Binary operator.  Got first operand in curr_token */
  292.       operand1 = curr_token.value.operand;
  293.       token_type = get_token ();
  294.       binary_operator = curr_token.value.operator;
  295.       if (token_type != TOK_OPERATOR || !is_binary_op (binary_operator))
  296.         error ("factor: expected binary operator, found %s",
  297.                token_name (&curr_token));
  298.       if (get_token () != TOK_STRING)
  299.         error ("factor: expected operand for binary operator, found %s",
  300.                token_name (&curr_token));
  301.       operand2 = curr_token.value.operand;
  302.       if (is_string_op (binary_operator))
  303.         retval = string_op (binary_operator, operand1, operand2);
  304.       else if (is_num_op (binary_operator))
  305.         retval = num_op (binary_operator, operand1, operand2);
  306.       else
  307.         error ("factor: internal error: %s is neither string nor numeric op",
  308.                op_name (binary_operator));
  309.     }
  310.  
  311.   leave (("factor: %d", retval));
  312.   return retval;
  313. }
  314.  
  315.  
  316. static int
  317. string_op (int operator, const char *operand1, const char *operand2)
  318. {
  319.   int retval;
  320.   int compare_result = strcmp (operand1, operand2);
  321.   
  322.   enter (("string_op (\"%s\" %s \"%s\")", operand1, op_name (operator), operand2));
  323.   switch (operator)
  324.     {
  325.     case OP_STREQ: retval = compare_result == 0; break;
  326.     case OP_STRNE: retval = compare_result != 0; break;
  327.     case OP_STRGE: retval = compare_result >= 0; break;
  328.     case OP_STRGT: retval = compare_result > 0; break;
  329.     case OP_STRLE: retval = compare_result <= 0; break;
  330.     case OP_STRLT: retval = compare_result < 0; break;
  331.     default:
  332.       error ("string_op: unknown string operator %s", op_name (operator));
  333.       /* NOTREACHED */
  334.       break;
  335.     }
  336.   leave (("string_op", retval));
  337.   return retval;
  338. }
  339.  
  340.  
  341. static int
  342. num_op (int operator, const char *operand1, const char *operand2)
  343. {
  344.   int retval;
  345.   int value1, value2;
  346.  
  347.   enter (("num_op (%s %s %s)", operand1, op_name (operator), operand2));
  348.   if (!is_number (operand1, &value1))
  349.     error ("num_op: expected number, found %s", operand1);
  350.   if (!is_number (operand2, &value2))
  351.     error ("num_op: expected number, found %s", operand2);
  352.  
  353.   switch (operator)
  354.     {
  355.     case OP_NUMEQ: retval = value1 == value2; break;
  356.     case OP_NUMNE: retval = value1 != value2; break;
  357.     case OP_NUMGE: retval = value1 >= value2; break;
  358.     case OP_NUMGT: retval = value1 > value2; break;
  359.     case OP_NUMLE: retval = value1 <= value2; break;
  360.     case OP_NUMLT: retval = value1 < value2; break;
  361.     default:
  362.       error ("num_op: unknown numeric operator %s", op_name (operator));
  363.       /* NOTREACHED */
  364.       break;
  365.     }
  366.   leave (("num_op: %d", retval));
  367.   return retval;
  368. }
  369.  
  370.  
  371. static int
  372. file_op (int operator, const char *pathname)
  373. {
  374.   struct stat statb;
  375.   int retval;
  376.   int access_mode;
  377.  
  378.   enter (("file_op (%s \"%s\")", op_name (operator), pathname));
  379.   if (operator == OP_RFILE || operator == OP_WFILE || operator == OP_XFILE)
  380.     {
  381.       switch (operator)
  382.         {
  383.         case OP_RFILE: access_mode = R_OK; break;
  384.         case OP_WFILE: access_mode = W_OK; break;
  385.         case OP_XFILE: access_mode = X_OK; break;
  386.         /* NOTREACHED */
  387.         default: access_mode = F_OK; break;
  388.         }
  389.       retval = access (pathname, access_mode) >= 0;
  390.     }
  391.   else if (stat (pathname, &statb) < 0)
  392.     retval = 0;
  393.   else
  394.     {
  395.       switch (operator)
  396.         {
  397.         case OP_EXFILE: retval = 1; break;
  398.         case OP_REGFILE: retval = (statb.st_mode & S_IFMT) == S_IFREG; break;
  399.         case OP_DIRFILE: retval = (statb.st_mode & S_IFMT) == S_IFDIR; break;
  400.         case OP_BLKFILE: retval = (statb.st_mode & S_IFMT) == S_IFBLK; break;
  401.         case OP_CHRFILE: retval = (statb.st_mode & S_IFMT) == S_IFCHR; break;
  402.         case OP_LNKFILE: retval = (statb.st_mode & S_IFMT) == S_IFLNK; break;
  403.         case OP_FIFFILE: retval = (statb.st_mode & S_IFMT) == S_IFIFO; break;
  404.         default:
  405.           error ("file_op: unknown file operator %s", op_name (operator));
  406.           break;
  407.         }
  408.     }
  409.  
  410.   leave (("file_op: %d", retval));
  411.   return retval;
  412. }
  413.  
  414.  
  415. static int
  416. is_number (const char *string, int *value)
  417. {
  418.   const char *curchar;
  419.   int digit;
  420.   int sign;
  421.   
  422.   *value = 0;
  423.  
  424.   if (string[0] == '-')
  425.     {
  426.       sign = -1;
  427.       curchar = string + 1;
  428.     }
  429.   else
  430.     {
  431.       sign = 1;
  432.       curchar = string;
  433.     }
  434.  
  435.   /* Accumulate return value as negative number in all cases, because that
  436.    * makes overflow impossible when string denotes INT_MIN.  (Assumes two's
  437.    * complement representation of integers.)  */
  438.   if (*curchar == '\0' || !isdigit (*curchar))
  439.     return 0;
  440.   else
  441.     {
  442.       *value = -(*curchar - '0');
  443.       curchar++;
  444.     }
  445.       
  446.   for (; *curchar != '\0'; curchar++)
  447.     {
  448.       if (isdigit (*curchar))
  449.         {
  450.           digit = *curchar - '0';
  451.           if (*value < (INT_MIN + digit)/10)
  452.             {
  453.               message ("is_number: %s overflows integer range", string);
  454.               return 0;
  455.             }
  456.           else
  457.             *value = 10*(*value) - digit;
  458.         }
  459.       else
  460.         return 0;
  461.     }
  462.  
  463.   if (sign == 1 && *value == INT_MIN)
  464.       {
  465.         message ("is_number: %s just overflows positive integers", string);
  466.         return 0;
  467.       }
  468.   else if (sign == 1)
  469.     *value = -(*value);
  470.   
  471.   return 1;
  472. }
  473.  
  474.  
  475. static void
  476. set_tokens (int argc, const char *const *argv)
  477. {
  478.   tokens_left = argc - 1;
  479.   if (strcmp (argv[tokens_left], "]") == 0)
  480.     tokens_left--;
  481.     
  482.   tokens = argv + 1;  
  483.   token_index = 0;
  484.   token_saved = 0;
  485. }
  486.  
  487.  
  488. static int
  489. get_token (void)
  490. {
  491.   struct operator_name {
  492.     const char *name;
  493.     int value;
  494.   };
  495.   static const struct operator_name names[] = {
  496.     {"-a", OP_AND},     {"-o", OP_OR},      {"!", OP_NOT},
  497.     {"=", OP_STREQ},   {"!=", OP_STRNE},   {">=", OP_STRGE},
  498.     {">", OP_STRGT},    {"<=", OP_STRLE},   {"<", OP_STRLT},
  499.     {"-eq", OP_NUMEQ},  {"-ne", OP_NUMNE},  {"-ge", OP_NUMGE},
  500.     {"-gt", OP_NUMGT},  {"-le", OP_NUMLE},  {"-lt", OP_NUMLT},
  501.     {"-e", OP_EXFILE},  {"-f", OP_REGFILE}, {"-d", OP_DIRFILE},
  502.     {"-b", OP_BLKFILE}, {"-c", OP_CHRFILE}, {"-l", OP_LNKFILE},
  503.     {"-F", OP_FIFFILE}, {"-r", OP_RFILE},   {"-w", OP_WFILE},
  504.     {"-x", OP_XFILE},   {0, 0}
  505.   };
  506.   const struct operator_name *search;
  507.   const char *token_string;
  508.     
  509.   if (token_saved || curr_token.type == TOK_EOF)
  510.     ;
  511.   else if (!more_tokens ())
  512.     curr_token.type = TOK_EOF;
  513.   else if (strcmp (tokens[token_index], "(") == 0)
  514.     {
  515.       token_index++;
  516.       tokens_left--;
  517.       curr_token.type = TOK_LPAREN;
  518.     }
  519.   else if (strcmp (tokens[token_index], ")") == 0)
  520.     {
  521.       token_index++;
  522.       tokens_left--;
  523.       curr_token.type = TOK_RPAREN;
  524.     }
  525.   else
  526.     {
  527.       token_string = tokens[token_index];
  528.       token_index++;
  529.       tokens_left--;
  530.       for (search = names; search->name != 0; search++)
  531.         if (strcmp (token_string, search->name) == 0)
  532.           break;
  533.           
  534.       if (search->name == 0)
  535.         {
  536.           curr_token.type = TOK_STRING;
  537.           curr_token.value.operand = token_string;
  538.         }
  539.       else
  540.         {
  541.           curr_token.type = TOK_OPERATOR;
  542.           curr_token.value.operator = search->value;
  543.         }
  544.     }
  545.   token_saved = 0;
  546.   out (("get_token: Returning token %s", token_name (&curr_token)));
  547.   return curr_token.type;
  548. }
  549.  
  550.  
  551. static void
  552. unget_token (void)
  553. {
  554.   if (token_saved)
  555.     error ("unget_token: unget token when token was already saved");
  556.   token_saved = 1;
  557. }
  558.  
  559.  
  560. static void
  561. accept (int type)
  562. {
  563.   int token_type = get_token ();
  564.   if (token_type != type)
  565.     error ("accept: expected token type %s, found token %s",
  566.            type_name (token_type), token_name (&curr_token));
  567. }
  568.  
  569. static int
  570. more_tokens (void)
  571. {
  572.   return tokens_left != 0;
  573. }
  574.  
  575.  
  576. static int
  577. is_num_op (int operator)
  578. {
  579.   return operator == OP_NUMEQ || operator == OP_NUMNE ||
  580.     operator == OP_NUMGE || operator == OP_NUMGT || operator == OP_NUMLE ||
  581.     operator == OP_NUMLT;
  582. }
  583.  
  584.  
  585. static int
  586. is_string_op (int operator)
  587. {
  588.   return operator == OP_STREQ || operator == OP_STRNE ||
  589.     operator == OP_STRGE || operator == OP_STRGT || operator == OP_STRLE ||
  590.     operator == OP_STRLT;
  591. }
  592.  
  593.  
  594. static int
  595. is_unary_op (int operator)
  596. {
  597.   return operator == OP_NOT || operator == OP_EXFILE ||
  598.     operator == OP_REGFILE || operator == OP_DIRFILE ||
  599.     operator == OP_BLKFILE || operator == OP_CHRFILE ||
  600.     operator == OP_LNKFILE || operator == OP_FIFFILE ||
  601.     operator == OP_RFILE || operator == OP_WFILE ||
  602.     operator == OP_XFILE;
  603. }
  604.  
  605.  
  606. static int
  607. is_binary_op (int operator)
  608. {
  609.   return operator == OP_AND || operator == OP_OR ||
  610.     operator == OP_NUMEQ || operator == OP_NUMNE ||
  611.     operator == OP_NUMGE || operator == OP_NUMGT ||
  612.     operator == OP_NUMLE || operator == OP_NUMLT ||
  613.     operator == OP_STREQ || operator == OP_STRNE ||
  614.     operator == OP_STRGE || operator == OP_STRGT ||
  615.     operator == OP_STRLE || operator == OP_STRLT;
  616. }
  617.  
  618.  
  619. static __EXITING
  620. error (const char *format, ...)
  621. {
  622.   va_list args;
  623.  
  624.   fprintf (stderr, "%s: ", program_name);
  625.   va_start (args, format);
  626.   vfprintf (stderr, format, args);
  627.   va_end (args);
  628.   fprintf (stderr, "\n");
  629.   exit (EXIT_ERROR);
  630. }
  631.  
  632.  
  633. static void
  634. message (const char *format, ...)
  635. {
  636.   va_list args;
  637.  
  638.   fprintf (stderr, "%s: ", program_name);
  639.   va_start (args, format);
  640.   vfprintf (stderr, format, args);
  641.   va_end (args);
  642.   fprintf (stderr, "\n");
  643. }
  644.  
  645.  
  646. static const char *
  647. op_name (int operator)
  648. {
  649.   struct op_name {
  650.     const char *name;
  651.     int value;
  652.   };
  653.   static const struct op_name names[] = {
  654.     {"-a", OP_AND},     {"-o", OP_OR},      {"!", OP_NOT},
  655.     {"=", OP_STREQ},    {"!=", OP_STRNE},   {">=", OP_STRGE},
  656.     {">", OP_STRGT},    {"<=", OP_STRLE},   {"<", OP_STRLT},
  657.     {"-eq", OP_NUMEQ},  {"-ne", OP_NUMNE},  {"-ge", OP_NUMGE},
  658.     {"-gt", OP_NUMGT},  {"-le", OP_NUMLE},  {"-lt", OP_NUMLT},
  659.     {"-e", OP_EXFILE},  {"-f", OP_REGFILE}, {"-d", OP_DIRFILE},
  660.     {"-b", OP_BLKFILE}, {"-c", OP_CHRFILE}, {"-l", OP_LNKFILE},
  661.     {"-F", OP_FIFFILE}, {"-r", OP_RFILE},   {"-w", OP_WFILE},
  662.     {"-x", OP_XFILE},   {0, 0}
  663.   };
  664.   const struct op_name *search;
  665.   
  666.   for (search = names; search->value != 0; search++)
  667.     if (search->value == operator)
  668.       return search->name;
  669.   return 0;
  670. }
  671.  
  672.  
  673. static const char *
  674. type_name (int type)
  675. {
  676.   struct type_name {
  677.     int type;
  678.     const char *name;
  679.   };
  680.   static const struct type_name names[] = {
  681.     {TOK_EOF, "EOF"}, {TOK_OPERATOR, "operator"},
  682.     {TOK_STRING, "string"}, {TOK_LPAREN, "left parenthesis"},
  683.     {TOK_RPAREN, "right parenthesis"}, {0, 0}
  684.   };
  685.   const struct type_name *search;
  686.   
  687.   for (search = names; search->type != 0; search++)
  688.     if (search->type == type)
  689.       return search->name;
  690.   return 0;
  691. }
  692.  
  693.  
  694. static const char *
  695. token_name (const struct token *tok)
  696. {
  697.   if (tok->type == TOK_EOF || tok->type == TOK_LPAREN ||
  698.       tok->type == TOK_RPAREN)
  699.     return type_name (tok->type);
  700.   else if (tok->type == TOK_OPERATOR)
  701.     return op_name (tok->value.operator);
  702.   else
  703.     return tok->value.operand;
  704. }
  705.  
  706.   
  707. static int
  708. bool_to_status (int boolval)
  709. {
  710.   return boolval ? EXIT_SUCCESS : EXIT_FAILURE;
  711. }
  712.  
  713.  
  714.